package ReflectApply.apply3;

import ReflectApply.data.Person;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author Jimmy Zhan WORKSTATION
 * @date 2023/3/31 18:54
 * 说明：反射的应用：调用指定的方法或属性
 */

public class ReflectTest {
    /******************************************如下是调用指定的属性*****************************************/
    // public int age = 1;
    @Test
    public void test1() throws Exception {
        Class clazz = Class.forName("ReflectApply.data.Person");

        Person p1 = (Person)clazz.newInstance();

        // 1. 获取运行时类中指定名的属性
        Field ageField = clazz.getField("age");

        // 2. 获取或设置此属性的值
        ageField.set(p1, 2);
        System.out.println(ageField.get(p1));
    }

    // private String name;
    @Test
    public void test2() throws Exception {
        Class clazz = Class.forName("ReflectApply.data.Person");

        Person p1 = (Person)clazz.newInstance();

        // 1. 通过Class的实例调用getDeclaredField(String fieldName)获取运行时类中指定名的属性
        Field nameField = clazz.getDeclaredField("name");

        // 2. setAccessible(true)：确保此属性是可访问的
        // 设置为true表示绕过java安全检查，即绕过权限修饰符的限制
        nameField.setAccessible(true);

        // 3. 通过Field类的实例调用：
        // get(Object obj)              --->  获取操作
        // set(Object obj, Object value)--->  设置操作
        nameField.set(p1, "Jimmy");
        System.out.println(nameField.get(p1));
    }

    // private static String info;
    @Test
    public void test3() throws Exception {
        Class clazz = Class.forName("ReflectApply.data.Person");

        // 1. 通过Class的实例调用getDeclaredField(String fieldName)获取运行时类中指定名的属性
        Field infoField = clazz.getDeclaredField("info");

        // 2. setAccessible(true)：确保此属性是可访问的
        // 设置为true表示绕过java安全检查，即绕过权限修饰符的限制
        infoField.setAccessible(true);

        // 3. 通过Field类的实例调用：
        // get(Object obj)              --->  获取操作
        // set(Object obj, Object value)--->  设置操作
        infoField.set(Person.class, "我是占吉米");
        System.out.println(infoField.get(Person.class));
        // 因为是可变形参，所以必须要传一个参数，Person是类型，传Person.class表示Person类的对象
        // 或者啥也不传，传一个null
        // ！！！ 仅限于静态属性和类变量 ！！！
        infoField.set(null, "我是占吉米");
        System.out.println(infoField.get(null));
    }

    /******************************************如下是调用指定的方法*****************************************/

    // private String showNation(String nation,int age)
    @Test
    public void test4() throws Exception{
        Class clazz = Class.forName("ReflectApply.data.Person");

        Person p1 = (Person) clazz.newInstance();

        // 1. 获取指定的方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class, int.class);

        // 2. setAccessible()
        showNation.setAccessible(true);

        // 3.invoke()
        // invoke方法的返回值就是Method对应的方法的返回值
        // 如果Method对应的方法返回值是void，则invoke()返回值为null
        String str = (String)showNation.invoke(p1, "CHN", 10);
        System.out.println(str);
    }

    // public static void showInfo()
    @Test
    public void test5() throws Exception{
        Class clazz = Class.forName("ReflectApply.data.Person");

        // 1. 获取指定的方法
        Method showInfo = clazz.getDeclaredMethod("showInfo");

        // 2. setAccessible()
        showInfo.setAccessible(true);

        // 3. invoke()
        // invoke方法的返回值就是Method对应的方法的返回值
        // 如果Method对应的方法返回值是void，则invoke()返回值为null
        showInfo.invoke(null);
    }

    /******************************************如下是调用指定的构造器*****************************************/
    // private Person(String name, int age)
    @Test
    public void test6() throws Exception{
        Class clazz = Person.class;

        // 1.
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);

        // 2.
        constructor.setAccessible(true);

        // 3.
        Person pJimmy = (Person) constructor.newInstance("Jimmy", 24);

        System.out.println(pJimmy);
    }

    // 使用Constructor替换原有的使用Class调用newInstance()的方式创建对象

    @Test
    public void test7() throws Exception{
        Class clazz = Person.class;

        // 1.
        Constructor constructor = clazz.getDeclaredConstructor();

        // 2.
        constructor.setAccessible(true);

        // 3.
        Person pJimmy = (Person) constructor.newInstance();

        System.out.println(pJimmy);
    }
}
